Elasticsearch 是一个基于 Apache Lucene 的开源搜索和分析引擎,允许用户近实时地存储、搜索和分析数据。Pronto 是 eBay 托管 Elasticsearch 集群的平台,使 eBay 内部客户易于部署、运维和扩展 Elasticsearch 以进行全文搜索、实时分析和日志事件监控。今天 Pronto 管理着 60 多个 Elasticsearch 集群,达 2000 多个节点。日采集数据量达到 180 亿个文档,日均查询量达到 35 亿。该平台提供了创建、修复、安全、监控、告警和诊断的一整套功能。
虽然 Elasticsearch 专为快速查询而设计,但其性能在很大程度上取决于应用程序的场景、索引的数据量以及应用程序和用户查询数据的速度。本文总结了 Pronto 团队面临的挑战以及应对挑战所构建的流程和工具,还给出了对几种配置进行基准测试的一些结果。
迄今遇到的 Pronto/Elasticsearch 使用场景所面临的挑战包括:
为了帮助我们的客户应对这些挑战,Pronto 团队为用户案例上线和整个集群生命周期,针对性能测试、调优和监控构建了一套策略方法。
Pronto 团队为每种类型的机器和每个支持的 Elasticsearch 版本运行基准测试,收集性能数据,然后结合客户提供的信息,估算群集初始大小,包括:
在开始索引数据和运行查询之前,我们先考虑一下。索引到底表示什么?Elastic 的官方答案是“具有某种相似特征的文档集合”。因此,下一个问题是“应该使用哪些特征来对数据进行分组?应该把所有文档放入一个索引还是多个索引?”,答案是,这取决于使用的查询。以下是关于如何根据最常用的查询组织索引的一些建议。
{
    "query": {
        "bool": {
            "must": {
                "match": {
                    "title": "${title}"
                }
            },
            "filter": {
                "term": {
                    "region": "US"
                }
            }
        }
    }
}
在这种情况下,如果索引按照美国、欧盟等地区分成几个较小的索引,可以从查询中删除过滤子句,查询性能会更好。如果我们需要运行一个跨地区查询,我们可以将多个索引或通配符传递给 Elasticsearch。
例如,Elasticsearch 集群中存有数以百万记的订单数据,大多数查询都包含有买方 ID 作为限定从句。为每个买家创建索引是不可能的,所以我们不能通过买方 ID 将数据拆分成多个索引。一个合适的解决方案是,使用路由将具有相同买方 ID 的所有订单放入相同分片中。然后几乎所有的查询都可以在匹配路由键的分片内完成。
对于日志记录和监控等重度索引场景,索引性能是关键指标。这里有一些建议:

性能和刷新时间间隔之间的关系
从上图可以看出,随着刷新时间间隔的增加,吞吐量增加,响应时间减少。我们可以使用下面的请求来检查我们有多少段以及刷新和合并花了多少时间。
Index/_stats?filter_path=
                                indices.**.refresh,indices.**.segments,indices.**.merges-
                                **减少副本数量。**对于每个索引请求,Elasticsearch
                                需要将文档写入主分片和所有副本分片。显然,副本过多会减慢索引速度,但另一方面,这将提高搜索性能。我们将在本文后面讨论这个问题。
                            
 性能和副本数之间的关系
                                性能和副本数之间的关系
从上图可以看出,随着副本数增加,吞吐量下降,响应时间增加。
使用 Elasticsearch 的主要原因是支持搜索数据。用户应该能够快速找到他们正在寻找的信息。搜索性能取决于很多因素。
 查询和过滤器性能比较
                                查询和过滤器性能比较
 搜索性能和副本数之间的关系
                                搜索性能和副本数之间的关系
从上图可以看出,搜索吞吐量几乎与副本数量成线性关系。注意在这个测试中,测试集群有足够的数据节点来确保每个分片都有一个专有节点。如果这个条件不能满足,搜索吞吐量就不会这么好。
分片太少会使搜索无法扩展。例如,如果分片数设置为 1,则索引中的所有文档都将存储在一个分片中。对于每个搜索,只有一个节点能够参与计算。如果索引中的文件数量很多,查询会很耗时。从另一方面来说,创建的索引分片太多也会对性能造成不利影响,因为 Elasticsearch 需要在所有分片上运行查询(除非在请求中指定了路由键),然后提取并合并所有返回的结果。
根据我们的经验,如果索引小于 1G,可以将分片数设置为 1。对于大多数场景,我们可以将分片数保留为默认值 5,但是如果分片大小超过 30GB,我们应该增加分片 ,将索引分成更多的分片。创建索引后,分片数不能更改,但是我们可以创建新的索引并使用 reindex API 迁移数据。
我们测试了一个拥有 1 亿个文档,大约 150GB 的索引。我们使用了 100 个线程发送搜索请求。
 搜索性能和分片数量之间的关系
                                搜索性能和分片数量之间的关系
从上图可以看出,最优的分片数量为 11 个。开始时搜索吞吐量增大(响应时间减少),但随着分片数量的增加,搜索吞吐量减小(响应时间增加)。
请注意,在这个测试中,就像在副本数测试中一样,每个分片都有一个独占节点。如果这个条件不能满足,搜索吞吐量将不会像这个图那样好。
在这种情况下,我们建议你尝试一个小于最优值的分片数,因为如果分片多,并且使每个分片都有一个独占数据节点,那么就需要很多节点。
我们可以使用下面的请求来检查一个节点查询缓存是否生效。
GET index_name/_stats?filter_path=indices.**.query_cache
{
  "indices": {
    "index_name": {
      "primaries": {
        "query_cache": {
          "memory_size_in_bytes": 46004616,
          "total_count": 1588886,
          "hit_count": 515001,
          "miss_count": 1073885,
          "cache_size": 630,
          "cache_count": 630,
          "evictions": 0
        }
      },
      "total": {
        "query_cache": {
          "memory_size_in_bytes": 46004616,
          "total_count": 1588886,
          "hit_count": 515001,
          "miss_count": 1073885,
          "cache_size": 630,
          "cache_count": 630,
          "evictions": 0
        }
      }
    }
  }
}
分片查询缓存。
如果大多数查询是聚合查询,我们应该考虑
分片查询缓存
。分片查询缓存可以缓存聚合结果,以便 Elasticsearch 以低开销直接处理请求。有几件事情需要注意:
我们可以使用下面的请求来检查分片查询缓存是否有效。
GET index_name/_stats?filter_path=indices.**.request_cache
{
  "indices": {
    "index_name": {
      "primaries": {
        "request_cache": {
          "memory_size_in_bytes": 0,
          "evictions": 0,
          "hit_count": 541,
          "miss_count": 514098
        }
      },
      "total": {
        "request_cache": {
          "memory_size_in_bytes": 0,
          "evictions": 0,
          "hit_count": 982,
          "miss_count": 947321
        }
      }
    }
  }
}
如果某些单词在索引中经常使用,但不在默认停用词列表中,则可以使用截止频率来动态处理它们。
对于每一次变更,都需要运行性能测试来验证变更是否适用。因为 Elasticsearch 是一个 RESTful 服务,所以可以使用 Rally、Apache Jmeter 和 Gatling 等工具来运行性能测试。因为 Pronto 团队需要在每种类型的机器和 Elasticsearch 版本上运行大量的基准测试,而且需要在许多 Elasticsearch 集群上针对不同 Elasticsearch 配置参数运行性能测试,所以这些工具不能满足我们的要求。
Pronto 团队建立了基于 Gatling 的在线性能分析服务,帮助客户和我们运行性能测试和回归测试。该服务提供的功能有:
架构如下:
性能测试服务架构
用户可以查看每个测试的 Gatling 报告,并查看 Kibana 预定义的可视化图像,以便进一步分析和比较,如下所示。
 Gatling 报告
                                Gatling 报告
 Gatling 报告
                                Gatling 报告
本文总结了在设计满足高期望的采集和搜索性能的 Elasticsearch 集群时应该考虑的索引 / 分片 / 副本设计以及一些其他配置,还说明了 Pronto 如何在策略上帮助客户进行初始规模调整、索引设计和调优以及性能测试。截至今天,Pronto 团队已经帮助包括订单管理系统(OMS)和搜索引擎优化(SEO)在内的众多客户实现了苛刻的性能目标,从而为 eBay 的关键业务作出了贡献。
Elasticsearch 的性能取决于很多因素,包括文档结构、文档大小、索引设置 / 映射、请求率、数据集大小和查询命中次数等等。针对一种情况的建议不一定适用于另一种情况,因此,彻底进行性能测试、收集数据、根据负载调整配置以及优化集群以满足性能要求非常重要。
查看英文原文: https://www.ebayinc.com/stories/blogs/tech/elasticsearch-performance-tuning-practice-at-ebay/